package com.tinem.platform.web.auth.controller;

import com.tinem.platform.client.security.VerificationCodeClient;
import com.tinem.platform.module.pojo.co.SessionKeyEnum;
import com.tinem.platform.module.pojo.vo.auth.oauth2.BindReq;
import com.tinem.platform.module.pojo.vo.error.UserException;
import com.tinem.platform.module.pojo.vo.security.verification.code.VerificationReq;
import com.tinem.platform.module.starter.jpa.entity.oauth.Oauth2WechatOpenUserInfo;
import com.tinem.platform.module.starter.jpa.repository.oauth2.Oauth2WechatOpenUserInfoRepository;
import com.tinem.platform.module.starter.sdk.MessageCode;
import com.tinem.platform.web.auth.login.LoginService;
import com.tinem.platform.web.auth.login.oauth2.ext.OAuth2AuthenticationToken;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigInteger;
import java.util.stream.Collectors;

/**
 * @author fzh
 * @version v1
 * @program: platform
 * @className Oauth2Controller
 * @description TODO
 * @site 
 * @company 
 * @create 2020-09-28 23:44
 */
@Controller
@RequestMapping("/endpoint/oauth2")
public class Oauth2Controller {
    @Autowired
    OAuth2ClientProperties oAuth2ClientProperties;
    @Autowired
    private AuthStateCache authStateCache;
    @Autowired
    Oauth2WechatOpenUserInfoRepository oauth2WechatOpenUserInfoRepository;
    @Autowired
    LoginService loginService;

    @Autowired
    VerificationCodeClient verificationCodeClient;

    @RequestMapping("/{registration}/auth.do")
    public void auth(@PathVariable("registration") String registration, HttpServletResponse response) throws IOException {
        AuthRequest authRequest = getAuthRequest(registration);
        response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
    }
    @RequestMapping("/{registration}/callback.do")
    public String login(@PathVariable("registration") String registration, AuthCallback callback, HttpServletRequest request,HttpServletResponse response) {
        request.getSession().setAttribute(SessionKeyEnum.oauth2_type.getKey(),registration);

        AuthRequest authRequest = getAuthRequest(registration);
        AuthResponse authResponse =  authRequest.login(callback);
        AuthUser authUser = (AuthUser) authResponse.getData();
        request.getSession().setAttribute(SessionKeyEnum.oauth2_userinfo.getKey(),authUser);
        Oauth2WechatOpenUserInfo oauth2WechatOpenUserInfo = oauth2WechatOpenUserInfoRepository.findFirstByUnionid(authUser.getRawUserInfo().getString("unionid"));
        if(oauth2WechatOpenUserInfo == null){
            return "redirect:/endpoint/oauth2/bind.do";
        }
        OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(oauth2WechatOpenUserInfo.getUserId());
        request.getSession().setAttribute(SessionKeyEnum.oauth2_token.getKey(),oAuth2AuthenticationToken);
        return "redirect:/endpoint/oauth2/login.do";
    }
    @GetMapping("/bind.do")
    public String bind(){
        return "oauth2/bind";
    }

    @PostMapping("/bind.do")
    public String bind(HttpServletRequest request,HttpServletResponse response,BindReq bindReq){
        String mobile = bindReq.getTo();
        Long codeId = (Long) request.getSession().getAttribute(SessionKeyEnum.verification_code_send.getKey(mobile));
        if(!verificationCodeClient.verification(new VerificationReq(mobile,codeId,bindReq.getCode())).isSuccess()){
            throw new UserException(MessageCode.ERROR_PLATFORM_CLOUD_WEB_AUTH_CODE);
        }
        AuthUser authUser = (AuthUser)request.getSession().getAttribute(SessionKeyEnum.oauth2_userinfo.getKey());
        String userId = loginService.bindUserByMobile(bindReq.getEmail(),"86",mobile,authUser.getUsername(),authUser.getNickname());
        OAuth2AuthenticationToken oAuth2AuthenticationToken = new OAuth2AuthenticationToken(userId);
        request.getSession().setAttribute(SessionKeyEnum.oauth2_token.getKey(),oAuth2AuthenticationToken);
        return "redirect:/endpoint/oauth2/login.do";
    }

    private AuthRequest getAuthRequest(String registration) {
        AuthRequest authRequest = null;
        switch (registration) {
            case "dingtalk":
                authRequest = new AuthDingTalkRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "baidu":
                authRequest = new AuthBaiduRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "github":
                authRequest = new AuthGithubRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build(), authStateCache);
                break;
            case "gitee":
                authRequest = new AuthGiteeRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build(), authStateCache);
                break;
            case "weibo":
                authRequest = new AuthWeiboRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "coding":
                authRequest = new AuthCodingRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "oschina":
                authRequest = new AuthOschinaRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "alipay":
                // 支付宝在创建回调地址时，不允许使用localhost或者127.0.0.1，所以这儿的回调地址使用的局域网内的ip
                authRequest = new AuthAlipayRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "qq":
                authRequest = new AuthQqRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "wechat_open":
                authRequest = new AuthWeChatOpenRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "taobao":
                authRequest = new AuthTaobaoRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "google":
                authRequest = new AuthGoogleRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "facebook":
                authRequest = new AuthFacebookRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "douyin":
                authRequest = new AuthDouyinRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "linkedin":
                authRequest = new AuthLinkedinRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "microsoft":
                authRequest = new AuthMicrosoftRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "mi":
                authRequest = new AuthMiRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "toutiao":
                authRequest = new AuthToutiaoRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "teambition":
                authRequest = new AuthTeambitionRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "pinterest":
                authRequest = new AuthPinterestRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "renren":
                authRequest = new AuthRenrenRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "stack_overflow":
                authRequest = new AuthStackOverflowRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "huawei":
                authRequest = new AuthHuaweiRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "wechat_enterprise":
                authRequest = new AuthWeChatEnterpriseRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "kujiale":
                authRequest = new AuthKujialeRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "gitlab":
                authRequest = new AuthGitlabRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "meituan":
                authRequest = new AuthMeituanRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "eleme":
                authRequest = new AuthElemeRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "twitter":
                authRequest = new AuthTwitterRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "wechat_mp":
                authRequest = new AuthWeChatMpRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            case "aliyun":
                authRequest = new AuthAliyunRequest(AuthConfig.builder()
                        .clientId(oAuth2ClientProperties.getRegistration().get(registration).getClientId())
                        .clientSecret(oAuth2ClientProperties.getRegistration().get(registration).getClientSecret())
                        .redirectUri(oAuth2ClientProperties.getRegistration().get(registration).getRedirectUri())
                        .scopes(oAuth2ClientProperties.getRegistration().get(registration).getScope().stream().collect(Collectors.toList()))
                        .build());
                break;
            default:
                break;
        }
        if (null == authRequest) {
            throw new AuthException("未获取到有效的Auth配置");
        }
        return authRequest;
    }

}
